home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Communications / pcomm / Source / x_send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  11.4 KB  |  523 lines

  1. /*
  2.  * Send a list of files using a version of Ward Christensen's file
  3.  * transfer protocol.  A non-zero return code means an error must be
  4.  * acknowledged by the user (a user generated abort returns a 0).
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include <curses.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>
  11. #include "config.h"
  12. #include "dial_dir.h"
  13. #include "misc.h"
  14. #include "xmodem.h"
  15.  
  16. static int tot_err, err_method;
  17.  
  18. int
  19. send_xmodem(win, list, type, fast)
  20. WINDOW *win;
  21. char *list;
  22. int type, fast;
  23. {
  24.     extern char *protocol[];
  25.     FILE *fp, *my_fopen();
  26.     int i, block_size, file_count, secs, mins, hours, big_blocks;
  27.     int small_blocks, err_count, got_it, num, is_batch, code;
  28.     int max_block, default_err;
  29.     static int rcv_first();
  30.     long size, block, sent, xmit_size;
  31.     char *file, *strtok(), *name, *strrchr();
  32.     unsigned short crc, calc_crc();
  33.     unsigned char buf[1029], blk, calc_sum();
  34.     unsigned int packet, sleep();
  35.     float performance, percent;
  36.     struct stat stbuf;
  37.                     /* which protocol? */
  38.     switch (type) {
  39.         case XMODEM:
  40.             is_batch = 0;
  41.             default_err = CRC_CHECKSUM;
  42.             max_block = 128;
  43.             performance = 1.36;
  44.             break;
  45.         case XMODEM_1k:
  46.             is_batch = 0;
  47.             default_err = CRC_CHECKSUM;
  48.             max_block = 1024;
  49.             performance = 1.09;
  50.             break;
  51.         case MODEM7:
  52.             is_batch = 1;
  53.             default_err = CHECKSUM;
  54.             max_block = 128;
  55.             performance = 1.36;
  56.             break;
  57.         case YMODEM:
  58.             is_batch = 1;
  59.             default_err = CRC;
  60.             max_block = 1024;
  61.             performance = 1.09;
  62.             break;
  63.         case YMODEM_G:
  64.             is_batch = 1;
  65.             default_err = NONE;
  66.             max_block = 1024;
  67.             performance = 1.02;
  68.             break;
  69.         default:
  70.             return(1);
  71.     }
  72.  
  73.     tot_err = 0;
  74.     file_count = 0;
  75.     mvwaddstr(win, 2, 24, protocol[type]);
  76.     mvwaddstr(win, 11, 24, "0  ");
  77.  
  78.                     /* each one in the list */
  79.     file = strtok(list, " \t");
  80.     do {
  81.                     /* is it a batch type? */
  82.         file_count++;
  83.         if (file_count > 1 && !is_batch)
  84.             break;
  85.                     /* display the name */
  86.         clear_line(win, 3, 24, TRUE);
  87.         if ((name = strrchr(file, '/')))
  88.             name++;
  89.         else
  90.             name = file;
  91.         waddstr(win, name);
  92.         wrefresh(win);
  93.                     /* get the file size */
  94.         if (stat(file, &stbuf) < 0) {
  95.             beep();
  96.             clear_line(win, 12, 24, TRUE);
  97.             wattrstr(win, A_BOLD, "CAN'T FIND FILE");
  98.             wrefresh(win);
  99.             sleep(3);
  100.             continue;
  101.         }
  102.                     /* sanity checking */
  103.         if ((stbuf.st_mode & S_IFREG) != S_IFREG) {
  104.             beep();
  105.             clear_line(win, 12, 24, TRUE);
  106.             wattrstr(win, A_BOLD, "NOT REGULAR FILE");
  107.             wrefresh(win);
  108.             sleep(3);
  109.             continue;
  110.         }
  111.  
  112.         size = stbuf.st_size;
  113.         mvwprintw(win, 4, 24, "%-10ld", size);
  114.         clear_line(win, 5, 24, TRUE);
  115.  
  116.         if (!(fp = my_fopen(file, "r"))) {
  117.             beep();
  118.             clear_line(win, 12, 24, TRUE);
  119.             wattrstr(win, A_BOLD, "PERMISSION DENIED");
  120.             wrefresh(win);
  121.             sleep(3);
  122.             continue;
  123.         }
  124.                     /* get the xmit size */
  125.         block_size = max_block;
  126.         big_blocks = 0;
  127.         small_blocks = 0;
  128.         if (block_size == 128) {
  129.             small_blocks = size / 128;
  130.             if (size % 128)
  131.                 small_blocks++;
  132.         }
  133.         else {
  134.             big_blocks = size / 1024;
  135.             small_blocks = (size % 1024) / 128;
  136.             if (size % 128)
  137.                 small_blocks++;
  138.  
  139.             if (small_blocks == 8 && !big_blocks) {
  140.                 big_blocks++;
  141.                 small_blocks = 0;
  142.             }
  143.                     /* if tiny file */
  144.             if (big_blocks == 0)
  145.                 block_size = 128;
  146.         }
  147.  
  148.         xmit_size = ((unsigned int) big_blocks * 1024L) + ((unsigned int) small_blocks * 128L);
  149.                     /* add block 0 to the size */
  150.         if (type == YMODEM || type == YMODEM_G)
  151.             xmit_size += 128L;
  152.  
  153.         secs = (xmit_size * 10.0 / dir->baud[dir->d_cur]) * performance;
  154.         hours = secs / 3600;
  155.         mins = (secs % 3600) / 60;
  156.         secs = (secs % 3600) % 60;
  157.  
  158.         mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
  159.  
  160.                     /* some starting numbers */
  161.         mvwaddstr(win, 7, 24, "     ");
  162.         mvwaddstr(win, 8, 24, "0%  ");
  163.         mvwaddstr(win, 9, 24, "0          ");
  164.         mvwaddstr(win, 10, 24, "0 ");
  165.         clear_line(win, 12, 24, TRUE);
  166.         waddstr(win, "NONE");
  167.         wrefresh(win);
  168.                     /* send the batch stuff */
  169.         switch (type) {
  170.             case MODEM7:
  171.                 if (code = rcv_first(win, default_err)) {
  172.                     fclose(fp);
  173.                     return(code +1);
  174.                 }
  175.  
  176.                 if (send_modem7(win, name)) {
  177.                     fclose(fp);
  178.                     return(1);
  179.                 }
  180.                 break;
  181.             case YMODEM:
  182.             case YMODEM_G:
  183.                 if (code = rcv_first(win, default_err)) {
  184.                     fclose(fp);
  185.                     return(code +1);
  186.                 }
  187.  
  188.                 if (code = send_ymodem(win, name, size)) {
  189.                     fclose(fp);
  190.                     /*
  191.                      * CANCEL now means that the other
  192.                      * end can't open that file.
  193.                      */
  194.                     if (code == CANCEL)
  195.                         break;
  196.                     return(code +1);
  197.                 }
  198.                 xmit_size -= 128L;
  199.                 break;
  200.             default:
  201.                 code = 0;
  202.                 break;
  203.         }
  204.                     /* remote can't receive that file? */
  205.         if (code == CANCEL)
  206.             break;
  207.                     /* wait for first character */
  208.         if (code = rcv_first(win, default_err)) {
  209.             fclose(fp);
  210.             return(code +1);
  211.         }
  212.                     /* here we go... */
  213.         clear_line(win, 12, 24, TRUE);
  214.         waddstr(win, "NONE");
  215.         wrefresh(win);
  216.         sent = 0L;
  217.         block = 1L;
  218.         blk = 1;
  219.         while (num = fread((char *) &buf[3], sizeof(char), block_size, fp)) {
  220.  
  221.                     /* fill short block */
  222.             if (num < block_size) {
  223.                 for (i=num; i<block_size; i++)
  224.                     buf[i+3] = CTRLZ;
  225.             }
  226.  
  227.                     /* show current stats */
  228.             mvwprintw(win, 7, 24, "%-5ld", block);
  229.             if (fast) {
  230.                 percent = sent * 100.0 / xmit_size;
  231.                 mvwprintw(win, 8, 24, "%0.1f%%", percent);
  232.                 mvwprintw(win, 9, 24, "%-10ld", sent);
  233.             }
  234.             wrefresh(win);
  235.  
  236.                     /* build the header */
  237.             if (block_size == 128)
  238.                 buf[0] = SOH;
  239.             else
  240.                 buf[0] = STX;
  241.  
  242.             buf[1] = blk;
  243.             buf[2] = ~blk;
  244.  
  245.                     /* build the error detection stuff */
  246.             switch (err_method) {
  247.                 case CHECKSUM:
  248.                     buf[block_size+3] = calc_sum(&buf[3], block_size);
  249. #ifdef DEBUG
  250.                     fprintf(stderr, "blk=%d, checksum=%d\n", blk, buf[block_size+3]);
  251. #endif /* DEBUG */
  252.                     packet = block_size +4;
  253.                     break;
  254.                 case CRC:
  255.                     crc = calc_crc(&buf[3], block_size);
  256.                     buf[block_size+3] = crc >> 8;
  257.                     buf[block_size+4] = crc;
  258. #ifdef DEBUG
  259.                     fprintf(stderr, "blk=%d, crc1=%d, crc2=%d\n", blk, buf[block_size+3], buf[block_size+4]);
  260. #endif /* DEBUG */
  261.                     packet = block_size +5;
  262.                     break;
  263.                 case NONE:
  264.                     buf[block_size+3] = 0;
  265.                     buf[block_size+4] = 0;
  266.                     packet = block_size +5;
  267.                     break;
  268.             }
  269.  
  270.                     /* send the block */
  271.             if (code = send_block(win, buf, packet)) {
  272.                 fclose(fp);
  273.                 return(code +1);
  274.             }
  275.             block++;
  276.             blk++;
  277.             sent = sent + (unsigned int) block_size;
  278.  
  279.                     /* change block size? */
  280.             if (xmit_size - sent < 1024)
  281.                 block_size = 128;
  282.         }
  283.         mvwaddstr(win, 8, 24, "100%  ");
  284.         mvwprintw(win, 9, 24, "%-10ld", sent);
  285.                     /* at the end of the file */
  286.         err_count = 0;
  287.         got_it = 0;
  288.         while (err_count < MAX_ERRORS) {
  289.             putc_line(EOT);
  290.             if (getc_line(10) == ACK) {
  291.                 got_it++;
  292.                 break;
  293.             }
  294.             err_count++;
  295.         }
  296.         clear_line(win, 12, 24, TRUE);
  297.         if (!got_it) {
  298.             /*
  299.              * So what???  We don't do anything if there is
  300.              * no acknowledge from the host!!
  301.              */
  302.             waddstr(win, "NO ACKNOWLEDGE");
  303.         }
  304.         else
  305.             waddstr(win, "TRANSFER COMPLETE");
  306.         if (!is_batch)
  307.             beep();
  308.         wrefresh(win);
  309.         sleep(2);
  310.                     /* prepare to start again */
  311.         fclose(fp);
  312.     } while (file = strtok((char *) NULL, " \t"));
  313.  
  314.     /*
  315.      * The end of batch markers... For modem7 it's an ACK and EOT, for
  316.      * ymodem, it's an empty block 0.
  317.      */
  318.     switch (type) {
  319.         case MODEM7:
  320.             if (code = rcv_first(win, default_err))
  321.                 return(code +1);
  322.             putc_line(ACK);
  323.             putc_line(EOT);
  324.             beep();
  325.             wrefresh(win);
  326.             break;
  327.         case YMODEM:
  328.         case YMODEM_G:
  329.             if (code = rcv_first(win, default_err))
  330.                 return(code +1);
  331.  
  332.             if (code = send_ymodem(win, "", 0L))
  333.                 return(code +1);
  334.             beep();
  335.             wrefresh(win);
  336.             break;
  337.         default:
  338.             break;
  339.     }
  340.     return(0);
  341. }
  342.  
  343. /*
  344.  * Wait for the first character to start the transmission.  This first
  345.  * character also sets the crc/checksum method.  Returns the standard
  346.  * error codes, or 0 on success.  The variable err_method is global.
  347.  */
  348.  
  349. static int
  350. rcv_first(win, default_err)
  351. WINDOW *win;
  352. int default_err;
  353. {
  354.     int i, err_count;
  355.     unsigned int sleep();
  356.     void cancel_xfer();
  357.  
  358.     err_count = 0;
  359.     while (err_count < MAX_ERRORS) {
  360.  
  361.                     /* scan the keyboard for abort */
  362.         if (wgetch(win) == ESC) {
  363.             beep();
  364.             clear_line(win, 12, 24, TRUE);
  365.             waddstr(win, "ABORTED");
  366.             wrefresh(win);
  367.             cancel_xfer(UP_LOAD);
  368.             sleep(3);
  369.             return(ABORT);
  370.         }
  371.                     /* scan the TTY line */
  372.         i = getc_line(10);
  373. #ifdef DEBUG
  374.         fprintf(stderr, "rcv_first: got \"%c\", %02x, %03o, %d\n", i, i, i, i);
  375. #endif /* DEBUG */
  376.         switch (i) {
  377.             case -1:    /* timed out */
  378.                 clear_line(win, 12, 24, TRUE);
  379.                 wattrstr(win, A_BOLD, "NO RESPONSE");
  380.                 err_count++;
  381.                 break;
  382.             case NAK:    /* checksum marker */
  383.                 if (default_err == CHECKSUM || default_err == CRC_CHECKSUM) {
  384.                     mvwaddstr(win, 5, 24, "CHECKSUM");
  385.                     err_method = CHECKSUM;
  386.                     return(0);
  387.                 }
  388.                 err_count++;
  389.                 break;
  390.             case 'C':    /* CRC marker */
  391.                 if (default_err == CRC_CHECKSUM || default_err == CRC) {
  392.                     mvwaddstr(win, 5, 24, "CRC");
  393.                     err_method = CRC;
  394.                     return(0);
  395.                 }
  396.                 err_count++;
  397.                 break;
  398.             case 'G':    /* ymodem-g marker */
  399.                 if (default_err == NONE) {
  400.                     mvwaddstr(win, 5, 24, "NONE");
  401.                     err_method = NONE;
  402.                     return(0);
  403.                 }
  404.                 err_count++;
  405.                 break;
  406.             case CAN:    /* two CAN's and you're out! */
  407.                 if (getc_line(2) == CAN) {
  408.                     beep();
  409.                     clear_line(win, 12, 24, TRUE);
  410.                     wattrstr(win, A_BOLD, "REMOTE ABORTED");
  411.                     wrefresh(win);
  412.                     return(CANCEL);
  413.                 }
  414.                 err_count++;
  415.                 break;
  416.             default:
  417.                 clear_line(win, 12, 24, TRUE);
  418.                 waddstr(win, "BAD HEADER");
  419.                 err_count++;
  420.                 break;
  421.         }
  422.         mvwprintw(win, 10, 24, "%-2d", err_count);
  423.         wrefresh(win);
  424.     }
  425.                     /* failed to get it right? */
  426.     beep();
  427.     clear_line(win, 12, 24, TRUE);
  428.     wattrstr(win, A_BOLD, "TIMED OUT");
  429.     wrefresh(win);
  430.     return(ERROR);
  431. }
  432.  
  433. /*
  434.  * Send a block of data, scan the keyboard for a user abort, and check
  435.  * the return codes from the host.  Returns standard error codes or 0
  436.  * on success.
  437.  */
  438.  
  439. int
  440. send_block(win, blk, packet)
  441. WINDOW *win;
  442. unsigned char *blk;
  443. unsigned int packet;
  444. {
  445.     extern int fd;
  446.     int i, err_count;
  447.     void cancel_xfer();
  448.     unsigned int sleep();
  449.  
  450.     err_count = 0;
  451.     mvwaddstr(win, 10, 24, "0 ");
  452.  
  453.     while (err_count < MAX_ERRORS) {
  454.                     /* write the block */
  455.         write(fd, (char *) blk, packet);
  456.                     /* scan the keyboard */
  457.         if (wgetch(win) == ESC) {
  458.             beep();
  459.             clear_line(win, 12, 24, TRUE);
  460.             waddstr(win, "ABORTED");
  461.             wrefresh(win);
  462.             cancel_xfer(UP_LOAD);
  463.             sleep(3);
  464.             return(ABORT);
  465.         }
  466.                     /* ymodem-g doesn't need ACKs */
  467.         if (err_method == NONE)
  468.             return(0);
  469.                     /* wait for acknowledge */
  470.         i = getc_line(10);
  471. #ifdef DEBUG
  472.         fprintf(stderr, "send_block: got \"%c\", %02x, %03o, %d\n", i, i, i, i);
  473. #endif /* DEBUG */
  474.         switch (i) {
  475.             case -1:    /* timed out */
  476.                 clear_line(win, 12, 24, TRUE);
  477.                 waddstr(win, "NO RESPONSE");
  478.                 err_count++;
  479.                 tot_err++;
  480.                 break;
  481.             case ACK:    /* Hooray!! we got it */
  482.                 return(0);
  483.             case NAK:    /* show our disappointment... */
  484.                 clear_line(win, 12, 24, TRUE);
  485.                 if (err_method == CRC)
  486.                     waddstr(win, "CRC FAILED");
  487.                 else
  488.                     waddstr(win, "CHECKSUM FAILED");
  489.                 err_count++;
  490.                 tot_err++;
  491.                 break;
  492.             case CAN:    /* two CAN's and you're out! */
  493.                 if (getc_line(2) == CAN) {
  494.                     beep();
  495.                     clear_line(win, 12, 24, TRUE);
  496.                     wattrstr(win, A_BOLD, "REMOTE ABORTED");
  497.                     wrefresh(win);
  498.                     return(CANCEL);
  499.                 }
  500.                 /* fall thru... */
  501.             default:
  502.                 clear_line(win, 12, 24, TRUE);
  503.                 waddstr(win, "RESENDING");
  504.                 err_count++;
  505.                 tot_err++;
  506.                 break;
  507.         }
  508.                     /* flush any pending garbage */
  509.         tty_flush(fd, 0);
  510.  
  511.         mvwprintw(win, 10, 24, "%-2d", err_count);
  512.         mvwprintw(win, 11, 24, "%-3d", tot_err);
  513.         wrefresh(win);
  514.     }
  515.                     /* failed to get it right */
  516.     beep();
  517.     clear_line(win, 12, 24, TRUE);
  518.     wattrstr(win, A_BOLD, "TOO MANY ERRORS");
  519.     wrefresh(win);
  520.     cancel_xfer(UP_LOAD);
  521.     return(ERROR);
  522. }
  523.